package client;

import java.io.IOException;
import java.net.Socket;
import java.util.Vector;

import nanoxml.XMLElement;
import utils.AbstractConnectionThread;
import utils.IConnection;
import utils.ITransmittable;
import utils.Player;
import utils.PlayerList;
import utils.Table;
import utils.User;

import commands.CommandArmy;
import commands.CommandChatMessage;
import commands.CommandGameResult;
import commands.CommandGameResultToOwner;
import commands.CommandJoinTableToOwner;
import commands.CommandLeaveTable;
import commands.CommandMessageToUser;
import commands.CommandMove;
import commands.CommandNewUserJoinTable;
import commands.CommandOtherPlayerName;
import commands.CommandPlayerDisconnected;
import commands.CommandReadyToStart;
import commands.CommandWeaponSelection;
import commands.Commands;

import enums.PlayerColor;

/**
 * Connects the game owner to the other player and viewers (And himself)
 */
public class ConnectionThreadTableOwnerToClient extends AbstractConnectionThread implements  IConnection {

	private TableOwnerServerThread tableServer = null;
	
	
	public  ConnectionThreadTableOwnerToClient(Socket clientSocket, TableOwnerServerThread tableServer){
		super (clientSocket);
		this.tableServer = tableServer;
	}
	

	public void init (String username) {
		this.username = username;
	}
	
	public void parse (XMLElement command) {
		try {
			if (command.getName().equals(Commands.LEAVE_TABLE)) {
				CommandLeaveTable commandLeaveTable= new CommandLeaveTable(command);
				leaveTable(commandLeaveTable);
			}
			else if (command.getName().equals(Commands.CHAT_MESSAGE)) {
				CommandChatMessage commandChatMessage = new CommandChatMessage(command);	
				commandChatMessage.setUsernameFrom(username);
				broadcast(username, commandChatMessage);
			}
			else if (command.getName().equals(Commands.JOIN_TABLE_TO_OWNER)) {
				CommandJoinTableToOwner commandJoinTableToOwner = new CommandJoinTableToOwner(command);	
				String joinedUsername = commandJoinTableToOwner.getUsername();
				init(joinedUsername);
				
				String tableOwnerName = tableServer.getTable().getOwnerUsername();
				Player tableOwnerPlayer = tableServer.getTable().getPlayerList().getPlayer(tableOwnerName);
				User joinedUser = tableServer.getConnToMainServer().getUserList().getUser(joinedUsername);
				tableServer.getConnections().addConnection(joinedUsername, this);
				
				if (!joinedUsername.equals(tableOwnerName)) {
					tableServer.getTable().addPlayer( joinedUser , PlayerColor.flipColor(tableOwnerPlayer.getColor()));
				}
				else {
					transmit(joinedUsername, tableServer.getTable());
				}
				
				broadcast(joinedUsername, new CommandNewUserJoinTable(joinedUsername));
			}
			else if (command.getName().equals(Commands.READY_TO_START)) {
				CommandReadyToStart commandReadyToStart = new CommandReadyToStart(command);	
				readyToStart (commandReadyToStart);
			}
			else if (command.getName().equals(Commands.ARMY)) {
				CommandArmy commandArmy = new CommandArmy(command);	
				broadcast(commandArmy.getUsername(), commandArmy );
			}
			else if (command.getName().equals(Commands.MOVE)) {
				CommandMove commandMove = new CommandMove(command);
				tableServer.getTable().moveTurn();
				broadcast(username, commandMove );
			}
			else if (command.getName().equals(Commands.WEAPON_SELECTION)) {
				CommandWeaponSelection commandWeaponSelection = new CommandWeaponSelection(command);
				String senderUsername = commandWeaponSelection.getUsername();
				broadcast(senderUsername ,commandWeaponSelection );
			}
			else if (command.getName().equals(Commands.MESSAGE_TO_USER)) {
				CommandMessageToUser commandMessageToUser = new CommandMessageToUser(command);
				sendToUser(commandMessageToUser.getUsernameTo(), commandMessageToUser.getMessage());
			}
			else if (command.getName().equals(Commands.GAME_RESULT_TO_OWNER)) {
				if (tableServer.getTable().isPlayerOwner(username)) {
					CommandGameResultToOwner commandGameResultToOwner = new CommandGameResultToOwner(command);
					String winnerColor = commandGameResultToOwner.getWinnerColor();
					String winner = null, loser = null;
					if (winnerColor.equals("Red")) {
						winner = tableServer.getTable().getOwnerUsername();
						loser = tableServer.getTable().getOtherPlayerUsername();
					}
					else {
						winner = tableServer.getTable().getOtherPlayerUsername();
						loser = tableServer.getTable().getOwnerUsername();
					} 
					tableServer.getConnToMainServer().send(new CommandGameResult(winner, loser));
				}
			}
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public Table getTable() {
		return tableServer.getTable();
	}

	@Override
	public String getUsername() {
		return tableServer.getUsername();
	}
	
	/**
	 * Removes the player or viewer the left, from the table.
	 * 
	 * @param commandLeaveTable - The {@link CommandLeaveTable} command issued by the player's or viewer's leaving the table.
	 */
	public void leaveTable(CommandLeaveTable commandLeaveTable) {
		String usernameLeaveTable = commandLeaveTable.getUsername();
		
		// Notify the other player and viewers that someone left the table
		broadcast(usernameLeaveTable ,commandLeaveTable);
		
		// If the owner quit, we need to close all its sockets to the table clients
		if (usernameLeaveTable.equals(tableServer.getTable().getOwnerUsername()))
		{
			tableServer.closeAllConnections();
		}
		// Else, we only need to close the socket to the user that quit
		else
		{
			try {
				// Stop the parsing loop in this connection thread
				stopLoop();
				
				// Close the socket to the game owner
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			
			if (usernameLeaveTable.equals(tableServer.getTable().getOtherPlayerUsername()))
			{
				tableServer.getTable().setOtherPlayerUsername("");
			}
		}
		
		tableServer.removePlayer(usernameLeaveTable);
		
		// Notify the server that someone left the table
		tableServer.getConnToMainServer().send(commandLeaveTable);
	}
	
	public void broadcast (ITransmittable element) {
		broadcast("", element);
	}
	
	public void broadcast(String username, ITransmittable command) {

		Vector<Player> players = tableServer.getTable().getPlayerList().getPlayers();
		for (int i = 0; i < players.size(); i++) {
			String currentUsername = players.get(i).getPlayerName();
			if 	(!currentUsername.equals(username)) {
				transmit(currentUsername, command);
			}
		} 
	}
	
	private void transmit(String destUsername, ITransmittable element) {
		IConnection conn = tableServer.getConnections().getConnection(destUsername);
		conn.send(element);
	}

	public void sendToUser(String usernameTo, String message)
	{
		ConnectionThreadTableOwnerToClient conn = (ConnectionThreadTableOwnerToClient)tableServer.getConnections().getConnection(usernameTo);
		conn.send(message);
	}

	@Override
	public void handleDisconnection()
	{
		// Notify to all the viewers that the player disconnected
		CommandPlayerDisconnected commandPlayerDisconnected = new CommandPlayerDisconnected(username, tableServer.getTable().getTableNum());
		broadcast(username, commandPlayerDisconnected);
		
		tableServer.removePlayer(commandPlayerDisconnected.getUsername());
		
		// Notify the server that someone left the table
		tableServer.getConnToMainServer().send(commandPlayerDisconnected);
	}
	
	private void readyToStart (CommandReadyToStart commandReadyToStart) {
		// Get the player
		String readyUsername = commandReadyToStart.getUsername(); 
		PlayerList players = tableServer.getTable().getPlayerList();
		Player playerReadyToStart = players.getPlayer (readyUsername);
		if (playerReadyToStart == null) {
			return;
		}
		
		// Change the player's state
		playerReadyToStart.readyToStart();
		
		broadcast(commandReadyToStart);
		
		// Check if this game is to start
		if (tableServer.getTable().isReadyToStart()) {
			
			// If not the owner pressed Start (Which for him happens automatically), then set the joiner's name as the other player that
			// will be playing
			if (readyUsername.equals(tableServer.getTable().getOwnerUsername()) == false)
			{
				tableServer.getTable().setOtherPlayerUsername(readyUsername);
				
				CommandOtherPlayerName commandOtherPlayerName = new CommandOtherPlayerName(readyUsername, tableServer.getTable().getTableNum());
				tableServer.getConnToMainServer().send(commandOtherPlayerName);
			}

			Vector<Player> playersVector = tableServer.getTable().getPlayerList().getPlayers();
			for (int i = 0; i < playersVector.size(); i++) {
				Player curPlayer = playersVector.get(i);
				if (curPlayer.getPlayerState() == Player.PLAYER_STATE_READY_TO_START) {
					curPlayer.startGame();
				}				
			}
			tableServer.getTable().setGameState(Table.PLAYING);
		}
	}
}
